/** @file res_font.cpp
	@brief Font resource */

#include "res/res_font.h"

using namespace G3;

//==============================================================================
// Only for tolua bindings
//==============================================================================
int G3FS_NumChars = 256;
int G3FS_NumCharsPerRow = 16;
int G3FS_NumCharStart = 32;
int G3FS_CharWidth = 16;
int G3FS_CharHeight = 16;
int G3FS_CharSet = 0;
float G3FS_Spacing = 10.0f;
std::string G3FS_BitmapFile;

//==============================================================================
// Constructors and Destructors
//==============================================================================
G3::FONT::FONT () {
    Log = &MainLog;

	Base = -1;

	NumChars = 256;
	NumCharsPerRow = 16;
	NumCharStart = 32;

	Width = 16;
	Height = 16;

	CharSet = 0;

	Spacing = 10.0f;
}

G3::FONT::~FONT () {
	Clear ();
}

TEXTBOX::TEXTBOX (FONT *fnt) {
	Font = fnt;

	Color = COL3F (1.0f, 1.0f, 1.0f);
	Position = VEC2I (10, 10);
}

//==============================================================================
// Builds a font from OpenGL texture
//==============================================================================
bool G3::FONT::glBuildFromTexture (H_TEXTURE Tex) {
    float cx = 0, cy = 0, fw = 0.0f, fh = 0.0f;

	if (Tex.IsNull ()) {
		if (Log)
			Log->Report ("ERROR: FONT::glBuildFromTexture: Texture Handle argument is invalid\n");
		return false;
	}

	if (TextureManager.GetTextureID (Tex) < 0) {
		if (Log)
			Log->Report ("WARNING: FONT::glBuildFromTexture: texture is not built. Defaulting filter to G3TF_LINEAR..\n");
		if (!TextureManager.BuildTexture2D (Tex, GTF_LINEAR))
			return false;
	}

	Texture = Tex;

    Base = glGenLists (NumChars);

    if (Log)
        Log->Report ("Font base: %d\n", Base);

	TextureManager.Apply (Tex);

    fw = 1.0f / ((float) Width);
    fh = 1.0f / ((float) Height);

    for (int i=0; i<NumChars; i++) {
        cx = 1.0f - ((float) (i % NumCharsPerRow) / ((float) Width));
        cy = 1.0f - ((float) (i / NumCharsPerRow) / ((float) Height));

        glNewList (Base + NumChars - 1 - i, GL_COMPILE);

        glBegin (GL_QUADS);
		    /* Texture Coord (Bottom Left) */
		    glTexCoord2f (cx - fw, cy);
			/* Vertex Coord (Bottom Left) */
			glVertex2i (0, 0);

			/* Texture Coord (Bottom Right) */
			glTexCoord2f (cx, cy);
			/* Vertex Coord (Bottom Right) */
			glVertex2i (Width, 0);

			/* Texture Coord (Top Right) */
			glTexCoord2f (cx, cy - fh);
			/* Vertex Coord (Top Right) */
			glVertex2i (Width, Height);

			/* Texture Coord (Top Left) */
			glTexCoord2f (cx - fw, cy - fh);
			/* Vertex Coord (Top Left) */
			glVertex2i (0, Height);
        glEnd ();
		// Character spacing
        glTranslatef (Spacing, 0, 0);

        glEndList ();
    }

	return true;
}

//==============================================================================
// Calculates the width of the text in pixels
//==============================================================================
int G3::FONT::CalcTextWidth (const string &Text) {
	return (int) (Spacing * Text.size ());
}

//==============================================================================
// Clips the text to a length in pixels
//==============================================================================
string G3::FONT::ClipTextRight (const string &aText, int aLength) {
	return aText.substr (0, (int) (aLength / Spacing));
}

string G3::FONT::ClipTextLeft (const string &aText, int aLength) {
	return aText.substr (aText.size () - (int) (aLength / Spacing), std::string::npos);
}

//==============================================================================
// Draws a text buffer using this font
//==============================================================================
void G3::FONT::DrawTextBuffer (const string &Buffer) {
	if (Base < 0 || Buffer.empty ())
		return;

	// Sets display lists offset
	glListBase (Base - NumCharStart + 128 * CharSet);
	// Renders the lists
	glCallLists (Buffer.size (), GL_BYTE, Buffer.c_str ());
}

//==============================================================================
// Loads a font from the stream
//==============================================================================
bool G3::FONT::Load (STREAM *aStream) {
	bool Result = false;
	TEXTURE_FILTER LastFilter = TextureManager.used_filter;

    if (aStream == NULL) {
        if (Log)
            Log->Report ("ERROR: FONT::Load: Trying to read a stream with a NULL pointer..\n");
        return false;
    }

	tolua_fontscript_open (MainLuaHandler.LuaState);

	aStream->DumpToLog ();

	if (MainLuaHandler.Execute (aStream) == -1) {
        if (Log)
            Log->Report ("ERROR: FONT::Load: Failed to execute the luascript..\n");
		return false;
	}

	NumChars = G3FS_NumChars;
	NumCharsPerRow = G3FS_NumCharsPerRow;
	NumCharStart = G3FS_NumCharStart;
	Width = G3FS_CharWidth;
	Height = G3FS_CharHeight;
	CharSet = G3FS_CharSet;
	Spacing = G3FS_Spacing;

	TextureManager.used_filter = G3::GTF_LINEAR;

	Result = glBuildFromTexture (TextureManager.GetHandle (G3FS_BitmapFile.c_str ()));

	TextureManager.used_filter = LastFilter;

	return Result;
}

//==============================================================================
// Loads a font by its script filename
//==============================================================================
bool G3::FONT::Load (const char *aFilename) {
    if (aFilename == NULL) {
        if (Log)
            Log->Report ("ERROR: FONT::Load: Filename argument is a NULL pointer..\n");
        return false;
    }

	string Path = MainFilesys.GetPath (aFilename);

	if (Path.empty ()) {
        if (Log)
            Log->Report ("Fontscript \"%s\" was not found\n", aFilename);

        return false;
	}

    if (Log)
        Log->Report ("Loading font \"%s\"\n", Path.c_str ());

	STREAM *fs = MainFileHandler.ReadWholeFile (Path.c_str ());

	if (!fs) {
        if (Log)
            Log->Report ("ERROR! FONT::Load: Failed to read the file \"%s\"\n", Path.c_str ());

        return false;
    }

	// Fontscripts are small - no need to stream them
	return Load (fs);
}

//==============================================================================
// Checks if the font has been successfully built
//==============================================================================
bool G3::FONT::IsBuilt () {
	return (!Texture.IsNull () && Base >= 0);
}

//==============================================================================
// Dumps the font into the log
//==============================================================================
void G3::FONT::Dump (G3::LOG *aLog) {
	if (aLog) {
		aLog->Report ("Dumping Font:\n");

		aLog->Report (" Base: %d\n", Base);
		aLog->Report (" NumChars: %d\n", NumChars);
		aLog->Report (" NumCharsPerRow: %d\n", NumCharsPerRow);
		aLog->Report (" NumCharStart: %d\n", NumCharStart);
		aLog->Report (" Width: %d\n", Width);
		aLog->Report (" Height: %d\n", Height);
		aLog->Report (" CharSet: %d\n", CharSet);
		aLog->Report (" Spacing: %f\n", Spacing);
		aLog->Report (" Texture: 0x%X\n", (uint) Texture);
	}
}

//==============================================================================
// Clears the Font
//==============================================================================
void G3::FONT::Clear () {
	if (Base >= 0)
	    glDeleteLists (Base, NumChars);
}

//==============================================================================
// Prints something into the textbox with Font
//==============================================================================
bool G3::TEXTBOX::glPrint (const char *fmt, ...) {
	char		text [1024];
	va_list		ap;
	static bool error_reported = false;

	if (!fmt)
		return false;

	Assert (Font, "TEXTBOX::glPrint: Font member is a NULL pointer");

	if (Font->Texture.IsNull () || Font->Base < 0) {
	    if (!error_reported) {
            error_reported = true;
            if (Font->Log)
                Font->Log->Report ("ERROR! TEXTBOX::glPrint: Font is not built..\n");
	    }
		return false;
	}

	va_start (ap, fmt);
	    vsprintf (text, fmt, ap);
	va_end (ap);

	// Get the viewport coordinates
	int ViewPort [4];
    glGetIntegerv (GL_VIEWPORT, ViewPort);

	glBlendFunc (GL_SRC_ALPHA, GL_ONE);

	glEnable (GL_BLEND);
	glEnable (GL_TEXTURE_2D);

	bool Lighting = false;

	if (glIsEnabled (GL_LIGHTING)){ glDisable (GL_LIGHTING); Lighting = true; }

	glBindTexture (GL_TEXTURE_2D, TextureManager.GetTextureID (Font->Texture));
  	glDisable (GL_DEPTH_TEST);
 		glSetPlanar (RECTI (0, ViewPort [3], ViewPort [2], 0), -1.0f, 1.0f);
			// TextBox position
			glTranslated (Position.x, ViewPort [3] - Position.y - Font->Height, 0);
			// TextBox color
			glColor3f (Color.r, Color.g, Color.b);
			// Sets display lists offset
			glListBase (Font->Base - Font->NumCharStart + 128 * Font->CharSet);
			// Renders the lists
			glCallLists (strlen (text), GL_BYTE, text);
		glRestorePerspective ();
    glEnable (GL_DEPTH_TEST);

	if (Lighting) { glEnable (GL_LIGHTING); }

	glDisable (GL_BLEND);

	return true;
}

